/*
 *  Copyright (C) 2010 Jonathan MERCIER <bioinfornatics@fedoraproject.org>,
 *  Louise-Amélie SCHMITT <louise-amelie.schmitt@etu.u-bordeaux1.fr>,
 *  Florence Maurier <florence_maurier@orange.fr>,
 *  Marianne Dussart <marianne.dussart@orange.fr>,
 *  Nicolas Fontaine <nifontaine@gmail.com>
 *
 *  JACoMode is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  JACoMode is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JACoMode.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.ubdx1.jacomode.models;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Collections;

public class Bilan
{
    private  ArrayList<String> bilans2              ;// overall results list (redundant)
    private  ArrayList<String> bilansid             ;// overall results id list
    private  ArrayList<String> bilansin             ;// substrates list
    private  ArrayList<String> bilansout            ;// products list
    private  ArrayList<String> bilanssame           ;// efm sharing the same overall result
    private  ArrayList<String> id_efms_in_bilans    ;// id of efm sharing the same overall result, sorted list, non-redundant
    private  ArrayList<String> bilans               ;// overall results'list
    private  ArrayList<Integer> nb_efms_in_bilans   ;// number of efm sharing the same overall result

    // id_efms_in_bilans and nb_efms_in_bilans are sorted in the same way

    private ArrayList<String> listmetabo            ;//list of metabolites belonging to the metabolic network
    private ArrayList<String> listmetaboin	    ;//list of substrates belonging to the metabolic network
    private ArrayList<String> listmetaboout         ;//list of products belonging to the metabolic network
    private ArrayList<String> metabobilansin        ;// substrates belonging to overall results containing the selected metabolite
    private ArrayList<String> metabobilansout       ;// products belonging to overall results containing the selected metabolite
    private ArrayList<String> metaboefm             ;// id of efm  containing the selected metabolite



    public Bilan()
    {
      bilans2              = new ArrayList<String>();
      bilansid             = new ArrayList<String>();
      bilansin             = new ArrayList<String>();
      bilansout            = new ArrayList<String>();
      bilanssame           = new ArrayList<String>();
      id_efms_in_bilans    = new ArrayList<String>();
      bilans               = new ArrayList<String>();
      nb_efms_in_bilans    = new ArrayList<Integer>();
      listmetabo           = new ArrayList<String>();
      listmetaboin         = new ArrayList<String>();
      listmetaboout        = new ArrayList<String>();
      metabobilansin       = new ArrayList<String>();
      metabobilansout      = new ArrayList<String>();
      metaboefm            = new ArrayList<String>();

    }

    /**
     * Calculates the overall reactions and fills the correspondent lists.
     * @param path path of the file .out
     */
    public void addBilan(String path)
     {
	ArrayList<String>   listmetabototal         = new ArrayList<String>();
        ArrayList<String>   listmetabototalin       = new ArrayList<String>();
        ArrayList<String>   listmetabototalout      = new ArrayList<String>();
        Scanner             s                       = null;
        Scanner             s2                      = null;
        String              bl                      = "";
        String              bl2                     = "";
        String              mot1                    = "ELEMENTARY MODES";
        String              mot2                    = " overall reaction";
        String              mot3                    = " ";
        String              egal                    = "";
        String              plus                    = "";
        boolean             balise                  = false;
        boolean             balise2                 = false;
        boolean             balise3                 = false;
        boolean             balise4                 = true;
        int                 idefm                   = 0;
        int                 efm                     = 0;
        Pattern             p1                      = Pattern.compile(mot1);
      	Pattern             p2                      = Pattern.compile(mot2);
      	Pattern             p3                      = Pattern.compile(mot3);
      	String              entree                  = "";
        String              sortie                  = "";
	try
	{
   		s = new Scanner(new BufferedReader(new FileReader(path)));
    	}
    catch(IOException error)
    {
		error.printStackTrace();
    }
    finally
    {
		while (s.hasNextLine())
        {
            bl = "";
            entree="";
            sortie="";
            bl = s.nextLine();
            Matcher m1 = p1.matcher(bl);

            boolean b1 = m1.matches();

            Matcher m2 =p2.matcher(bl);
            boolean b2 = m2.matches();

            Matcher m3 =p3.matcher(bl);
            boolean b3 = m3.matches();
            if (b1)
            {
                balise=true;
            }


            if (balise)
            {
                if (b2)
                {
                    bl = s.nextLine();
                    balise2=true;
                }
            }
            if (b3)
            {
                balise2=false;
            }
            if (balise2 && !b3)
            {
                String id="";
                int cop=0;
                try
                {
                    s2 = new Scanner(bl);
                }
                finally
                {
                    balise3=false;
                    while (s2.hasNext())
                    {
                        id="";
                        bl2=s2.next();
                        cop=bl2.length();
                        id=bl2.substring(cop-1,cop);
                        if (id.equals(":"))
                        {
                            bilansid.add(bl2.substring(0,cop-1));
                            bl2=s2.next();
                        }
                        if (bl2.equals("=") || bl2.equals("transformation"))
                        {
                            balise3=true;
                            bl2=s2.next();
                        }
                        if (!balise3)
                        {
                            entree=entree+" "+bl2;
                        }
                        if (balise3)
                        {
                            sortie=sortie+" "+bl2;
                        }


                    }
                    if (s2 != null)
                    {
                        s2.close();
                    }
                }

                bilans2.add(entree+" ="+sortie);
            }
	    }
		if (s != null)
        {
			s.close();
        }
    }

	bilans2.remove(0);
	String samebilan="";
	for(int bn = 0; bn < bilansid.size(); bn++)
    {
        for(int x = 0; x < bilansid.size(); x++)
        {

            if (bilans2.get(bn).equals(bilans2.get(x)))
            {
                samebilan=samebilan+" "+bilansid.get(x);
            }
        }

        bilanssame.add(samebilan);
        samebilan="";
    }
	Collections.sort(bilanssame);
	String indiceparen=bilanssame.get(0);
	id_efms_in_bilans.add(indiceparen);
	for(int bn = 0; bn < bilanssame.size(); bn++)
    {
		if (!indiceparen.equals(bilanssame.get(bn)))
        {
            id_efms_in_bilans.add(bilanssame.get(bn));
            indiceparen=bilanssame.get(bn);
        }
    }
	for(int bn = 0; bn < id_efms_in_bilans.size(); bn++)
    {
        balise4=true;
        efm=0;
        try
        {
			s2 = new Scanner(id_efms_in_bilans.get(bn));
        }
        finally
        {
			while (s2.hasNext())
            {

				bl="";
				bl=s2.next();
				idefm=Integer.parseInt(bl)-1;
				if (balise4)
                {
					bilans.add(bilans2.get(idefm));
                }
				balise4=false;
				efm++;
            }
            if (s2 != null)
            {
				s2.close();
            }
        }
        nb_efms_in_bilans.add(efm);
    }
	for(int bn = 0; bn < bilans.size(); bn++)
    {
        entree="";
        sortie="";
        try
        {
            s2 = new Scanner(bilans.get(bn));
        }
		finally
        {
			balise3=false;
			while (s2.hasNext())
            {
                bl2="";
                bl2=s2.next();
                if (bl2.equals("=") || bl2.equals("transformation"))
                {
                    balise3=true;
                    egal=bl2;
                }
				if (!balise3 )
                {

                entree=entree+" "+bl2;
                //
                }
				if (balise3 && ( !bl2.equals(egal) ) )
                {

                    sortie=sortie+" "+bl2;
                }
				if (bl2.equals("+"))
                {
                            plus=bl2;
                }
                            if (!balise3 && ( !bl2.equals(egal) ) && (!bl2.equals(plus)) )
				{
				    listmetabototalin.add(bl2);
				}
                            if (balise3 && (!bl2.equals(egal) ) && (!bl2.equals(plus) ) )
				{
				    listmetabototalout.add(bl2);
				}
                            if (( !bl2.equals(egal) ) && (!bl2.equals(plus) ) )
				{
				    listmetabototal.add(bl2);
				}
            }
			if (s2 != null)
            {
                s2.close();
            }
        }
		bilansin.add(entree);
		bilansout.add(sortie);
    }
	Collections.sort(listmetabototalin);
	indiceparen=listmetabototalin.get(0);
	Pattern p4 = Pattern.compile("[a-zA-Z]");
	String alpha="";
	String lettre="";
	int length=0;
	boolean chiffre=true;
	boolean intrus=false;
	for(int x = 0; x < listmetabototalin.size(); x++)
    {
		alpha=listmetabototalin.get(x);
		length=alpha.length();
		for (int i=0;i< length;i++)
        {
			lettre=alpha.substring(i, i+1);
			if ((lettre.matches("[a-zA-Z]" ))|| (lettre.matches("_")) )
            {
                chiffre=false;
            }
        }
		if (!indiceparen.equals(listmetabototalin.get(x)) && !chiffre)
        {
            if (listmetabototalin.get(x).equals("transfomation") || listmetabototalin.get(x).equals("net") ||listmetabototalin.get(x).equals("no") ||listmetabototalin.get(x).equals("of") ||listmetabototalin.get(x).equals("external") ||listmetabototalin.get(x).equals("metabolites") )//no net transfomation of external metabolites
            {
                intrus=true;
            }
            if (!intrus)
            {
                listmetaboin.add(listmetabototalin.get(x));
                indiceparen=listmetabototalin.get(x);
                //System.out.println("in"+listmetabototalin.get(x));
            }
        }
		intrus=false;
		chiffre=true;
    }
	Collections.sort(listmetabototal);
	indiceparen=listmetabototal.get(0);
	alpha="";
	lettre="";
	length=0;
	chiffre=true;
	intrus=false;
	for(int x = 0; x < listmetabototal.size(); x++)
    {
        alpha=listmetabototal.get(x);
        length=alpha.length();
        for (int i=0;i< length;i++)
        {
            lettre=alpha.substring(i, i+1);
            if ((lettre.matches("[a-zA-Z]" ))|| (lettre.matches("_")) )
            {
                chiffre=false;
            }
        }
        if (!indiceparen.equals(listmetabototal.get(x)) && !chiffre)
        {
            if (listmetabototal.get(x).equals("transfomation") || listmetabototal.get(x).equals("net") ||listmetabototal.get(x).equals("no") ||listmetabototal.get(x).equals("of") ||listmetabototal.get(x).equals("external") ||listmetabototal.get(x).equals("metabolites") )//no net transfomation of external metabolites
            {
                intrus=true;
            }
            if (!intrus)
            {
                listmetabo.add(listmetabototal.get(x));
                indiceparen=listmetabototal.get(x);
                //System.out.println("total"+listmetabototal.get(x));
            }
        }
        intrus=false;
        chiffre=true;
    }
	Collections.sort(listmetabototalout);
	indiceparen=listmetabototalout.get(0);
	alpha="";
	lettre="";
	length=0;
	chiffre=true;
	intrus=false;
	for(int x = 0; x < listmetabototalout.size(); x++)
    {
	    alpha=listmetabototalout.get(x);
	    length=alpha.length();
	    for (int i=0;i< length;i++)
        {
            lettre=alpha.substring(i, i+1);
            if ((lettre.matches("[a-zA-Z]" ))|| (lettre.matches("_")) )
            {
                chiffre=false;
            }
        }
	    if (!indiceparen.equals(listmetabototalout.get(x)) && !chiffre)
        {
            if (listmetabototalout.get(x).equals("transfomation") || listmetabototalout.get(x).equals("net") ||listmetabototalout.get(x).equals("no") ||listmetabototalout.get(x).equals("of") ||listmetabototalout.get(x).equals("external") ||listmetabototalout.get(x).equals("metabolites") )//no net transfomation of external metabolites
            {
                intrus=true;
            }
            if (!intrus)
            {
                listmetaboout.add(listmetabototalout.get(x));
                indiceparen=listmetabototalout.get(x);
                //System.out.println("out"+listmetabototalout.get(x));
            }
        }
        intrus=false;
        chiffre=true;
    }
}

    /**
     * Search the EFM containing a metabolite and put them in a list
     * @param metabolite metabolite name
     */
    public void metabolite(String metabolite)
    {
	ArrayList<Integer> listmetabobilan = new ArrayList<Integer>();// id of overall results containing the selected metabolite
	String bl="";
	Scanner s=null;
	//String metabo="ATP";
	Pattern meta = Pattern.compile(metabolite);

	for(int bn = 0; bn < bilans.size(); bn++)
    {
        try
        {
            s = new Scanner(bilans.get(bn));
        }
        finally
        {
            while (s.hasNext())
            {
                bl="";
                bl=s.next();
                Matcher m = meta.matcher(bl);
                boolean b1 = m.matches();
                if (b1)
                {
                    listmetabobilan.add(bn);
                }
            }
            if (s != null)
            {
                s.close();
            }

        }
    }
    for(int bn = 0; bn < listmetabobilan.size(); bn++)
    {
        metabobilansin.add(bilansin.get(listmetabobilan.get(bn)));
        metabobilansout.add(bilansout.get(listmetabobilan.get(bn)));
        metaboefm.add(id_efms_in_bilans.get(listmetabobilan.get(bn)));
    }
}

    /**
     * Clear the lists of overall reactions results
     */
    public void clean()
    {
        metabobilansin.clear();
        metabobilansout.clear();
        metaboefm.clear();

    }

    /**
     * Save the overall reactions results in a CSV file
     * @throws IOException if an I/O error occurs
     */
    public void csvBilan() throws IOException
	{
		File file = new File("Bilan.csv");
		FileWriter fw;
		fw = new FileWriter(file);
		ArrayList<String> bilansArray=new ArrayList<String>();
		bilansArray=getbilans();
		fw.write("Overall reaction"+"\n");
		fw.write("\n");
		fw.write("overall reaction, number of efms"+"\n");
		for (int x = 0; x < bilansArray.size(); x++)
		{
			fw.write(bilansArray.get(x)+", "+getBilanssame4().get(x)+"\n");
		}
		fw.close();
	}




    // Getters

    /**
     * Returns a list containing reactants of overall reactions.
     * @return a list containing reactants of overall reactions
     */
    public ArrayList<String> getBilansin()
    {
        return bilansin;
    }

    /**
     * Returns a list containing products of overall reactions
     * @return a list containing products of overall reactions
     */
    public ArrayList<String> getBilansout()
    {
        return bilansout;
    }

    /**
     * Returns a sorted out list containing all the id of the EFM of same overall reactions whithout repetition.
     * @return a sorted out list containing all the id of the EFM of same overall reactions whithout repetition
     */
    public ArrayList<String> getBilanssame2()
    {
        return id_efms_in_bilans;
    }
    
    /**
     * Returns a list containing the overall reactions whithout repetition.
     * @return a list containing the overall reactions whithout repetition
     */
    public ArrayList<String> getbilans()
    {
        return bilans;
    }

    /**
     * Returns a list containing the numbers of EFM having the same overall reaction.
     * @return a list containing the numbers of EFM having the same overall reaction
     */
    public ArrayList<Integer> getBilanssame4()
    {
        return nb_efms_in_bilans;
    }

    /**
     * Returns a list containing the overall reaction reactants containing the chosen metabolite.
     * @return a list containing the overall reaction reactants containing the chosen metabolite
     */
    public ArrayList<String> getMetabobilansin()
    {
        return metabobilansin;
    }

    /**
     * Returns a list containing the overall reaction products containing the chosen metabolite.
     * @return a list containing the overall reaction products containing the chosen metabolite
     */
    public ArrayList<String> getMetabobilansout()
    {
        return metabobilansout;
    }

   /**
     * Returns a list containing the id of the efm containing the chosen metabolite.
     * @return a list containing the id of the efm containing the chosen metabolite
     */
    public ArrayList<String> getMetaboefm()
    {
        return metaboefm;
    }

    /**
     * Returns a list containing the metabolites found in the network.
     * @return a list containing the metabolites found in the network
     */
    public ArrayList<String> getMetabo()
    {
        return listmetabo;
    }

    /**
     * Returns a list containing the entering metabolites found in the network.
     * @return a list containing the entering metabolites found in the network
     */
    public ArrayList<String> getMetaboIn()
    {
        return listmetaboin;
    }

    /**
     * Returns a list containing the outgoing metabolites found in the network.
     * @return a list containing the outgoing metabolites found in the network
     */
     public ArrayList<String> getMetaboOut()
    {
        return listmetaboout;
    }

}